#!/usr/bin/env python
# coding=utf-8
"""
@file: date_util.py
@contact: bianzhiwei@iyoujia.com
@time   : 2019/4/1 11:04 
@Desc   :
"""

import os
import time
import http
import email
import poplib
import smtplib
import datetime
import http.client
import pandas as pd
from urllib import parse
from email import encoders
from email.parser import Parser
from collections import OrderedDict
from email.header import Header, decode_header
from email.mime.base import MIMEBase
from email.mime.text import MIMEText
from report_system.utils import date_util
from report_system.utils import excel_util
from report_system.utils import style_util

from report_system.utils import mailbox_util
from report_system.utils.log_util import log
from email.mime.multipart import MIMEMultipart
from report_system.utils import development_util
from report_system.utils.development_util import debug

# 设置数据格式
pd.set_option('display.float_format', lambda x: '%.3f' % x)
# 第三方 SMTP 服务
mail_host = "smtp.mxhichina.com"  # 设置服务器
mail_pop3_ = 'pop3.mxhichina.com'
mail_user = "data@iyoujia.com"  # 用户名
mail_pass = "mayi@2199"  # 口令
sender = 'data@iyoujia.com'

pwd, filename = os.path.split(os.path.abspath(__file__))
# 当前文件的父路径
father_path = os.path.abspath(os.path.dirname(pwd) + os.path.sep + ".")


def decode_str(s):
    value, charset = decode_header(s)[0]
    if charset:
        value = value.decode(charset)
    return value


def send_files_mail(files, text, title, html=None, receiver='bianzhiwei@iyoujia.com', content_type='plain', tab=10,
                    invest=False):
    """
    发送附件信息
    :param files: 要发送的发送文件的集合信息
    :param title: 要发送的标题
    :param html: 要发送的html
    :param text: 发送的文件的内容
    :param receiver: 想要发送的目标人物  eg: 'aaa@iyoujia.com;bbb@iyoujia.com'
    :param content_type: content_type  默认 plain
    :param invest:  是否是投资人报表  如果是 则用投资人邮箱  否则用data邮箱
    :return:
    """
    try:
        if invest:
            mail_user = "investor_service@iyoujia.com"  # 用户名
            mail_pass = "youjia@2019"  # 口令
            sender = 'investor_service@iyoujia.com'
        else:
            mail_user = "data@iyoujia.com"  # 用户名
            mail_pass = "mayi@2199"  # 口令
            sender = 'data@iyoujia.com'

        # 创建一个带附件的实例
        message = MIMEMultipart()
        message['From'] = sender
        message['To'] = receiver
        message['Subject'] = Header(title, 'utf-8')
        # 邮件正文内容
        message.attach(MIMEText(text, content_type, 'utf-8'))

        if html:
            if text:
                html = text + html
            message.attach(MIMEText(html, 'html', 'utf-8'))

        contype = 'application/octet-stream'
        maintype, subtype = contype.split('/', 1)
        for file in files if isinstance(files, list) else str(files).split(';'):
            # att = MIMEBase('application', 'octet-stream')
            # att.set_payload(open(filename, 'rb').read())
            # att.add_header('Content-Disposition', 'attachment', filename=('gbk', '', filename))
            # encoders.encode_base64(att)

            # 构造附件
            log.info("构造的邮件文件 file 为: {file}".format(file=file))
            data = open(file.strip(), 'rb')
            file_msg = MIMEBase(maintype, subtype)
            file_msg.set_payload(data.read())
            data.close()
            encoders.encode_base64(file_msg)
            basename = os.path.basename(file)
            log.info("构造的邮件文件 basename 为: {basename}".format(basename=basename))
            # print(basename)
            # ('content-disposition', 'attachment', filename=('utf-8', '', Fußballer.ppt'))
            file_msg.add_header('Content-Disposition', 'attachment', filename=('gbk', '', basename))
            message.attach(file_msg)

        smtp_obj = smtplib.SMTP_SSL(host=mail_host)  # 注意：如果遇到发送失败的情况（提示远程主机拒接连接），这里要使用SMTP_SSL方法
        smtp_obj.connect(mail_host)
        smtp_obj.login(mail_user, mail_pass)
        smtp_obj.sendmail(sender, str(receiver).strip().split(";"), message.as_string())
        log.info("邮件发送成功！！！to = {}".format(receiver))
        log.info("发送成功的文件是 files = {}".format(files))
        smtp_obj.quit()
    except Exception as e:
        if tab > 0:
            time.sleep(1)
            log.error('发送失败！  正在尝试重新发送！请稍等。。。。。。')
            send_files_mail(files, text, title, html, receiver=receiver, content_type=content_type, tab=tab - 1)
        else:
            log.exception(e)


def send_tel_message(content, mobile):
    """
    发送短信短信
    :param content: 短信内容
    :param mobile: 电话号
    :return:
    """
    text = content
    host = "124.251.7.68"
    sms_send_uri = "/QxtSms/QxtFirewall"
    params = parse.urlencode(
        {'OperID': 'youjiatz', 'OperPass': 'TAvQb7HN', 'Content': text.encode("gbk"), 'DesMobile': str(mobile)})
    headers = {"Content-type": "application/x-www-form-urlencoded", "Accept": "text/plain"}
    conn = http.client.HTTPConnection(host, port=8000, timeout=30)

    try:
        conn.request("POST", sms_send_uri, params, headers)
        conn.getresponse()
        conn.close()
        log.info('短信发送成功')
    except Exception as e:
        log.exception(e)


def error_mail(message=None, err=None, receiver=None, mobiles=None, title='警报--邮件', tab=10):
    """
    警报邮件
    :param message: 邮件消息
    :param err: 报错内容
    :param receiver: 邮件接收人
    :param mobiles: 短信接收人
    :param title: 邮件标题
    :param tab:
    :return:
    """
    if debug:
        log.info("测试环境不发送 --报警-- 信息")
    else:
        content_type = 'plain'
        app_dir = development_util.get_app_dir()
        app_name = development_util.get_script_name()

        receiver = receiver if receiver else mailbox_util.error_mail
        mobiles = mobiles if mobiles else mailbox_util.error_mobiles

        text = app_dir + '\n' + app_name + '\n 出现异常! 请处理 \n' + message + '\n' + str(err)
        try:
            # 创建一个带附件的实例
            message = MIMEMultipart()
            message['From'] = sender
            message['To'] = receiver
            message['Subject'] = Header(title, 'utf-8')
            # 邮件正文内容
            message.attach(MIMEText(text, content_type, 'utf-8'))

            smtp_obj = smtplib.SMTP_SSL(host=mail_host)  # 注意：如果遇到发送失败的情况（提示远程主机拒接连接），这里要使用SMTP_SSL方法
            smtp_obj.connect(mail_host)
            smtp_obj.login(mail_user, mail_pass)
            smtp_obj.sendmail(sender, str(receiver).strip().split(";"), message.as_string())
            log.info("邮件发送成功！！！to = {}".format(receiver))
            smtp_obj.quit()
            for mobile in mobiles.split(';'):
                send_tel_message(content=text, mobile=mobile)
        except Exception as e:
            if tab > 0:
                time.sleep(1)
                log.error('发送失败！  正在尝试重新发送！请稍等。。。。。。')
                error_mail(message=message, err=err, receiver='dt@iyoujia.com', tab=tab - 1)
            else:
                log.exception(e)


# city_mails = {'北京': 'liuguanghua@iyoujia.com', '上海': 'bianzhiwei@iyoujia.com', }


def distribute_mail(file_path=None, df_dict=None, file_name=None, html_sheet_name=None, na_rep='', text=None,
                    title=None, receiver=None, colors_dict=None, content_type='plain', engine='openpyxl',
                    suffix='xlsx', city=False, header=1, city_key='index', city_mails=None, tab=10, index=True,
                    merge_cells=True, invest=False, uuid=None, freeze_panes=(1, 2), is_df=True):
    """
    将 df 转化为 excel 根据mis 系统中的成员的城市进行数据提取发送
    :param df_dict: 字典   key 为  sheet_name 单元格名称  value 为 单元格的df
    :param file_path : 要分发的邮件路径
    :param file_name: 主文件名
    :param header: 如果是文件  前几行为标题
    :param engine:
    :param html_sheet_name: 想要成为html 的sheet 页面
    :param suffix: 后缀  xls 或者 xlsx
    :param na_rep: 如果为null   则替换成 na_rep
    :param title: 要发送的标题
    :param text: 发送的文件的内容
    :param receiver: 想要发送的目标人物  eg: 'aaa@iyoujia.com;bbb@iyoujia.com'
    :param content_type: content_type  默认 plain
    :param tab:  发送的重试次数
    :param city_key:  df 中关键分词的主键
    :param colors_dict:  颜色的分类  {'sheet_name':{'一级标题':'颜色'}}
    :param city:  是否分城市发送
    :param city_mails:  分城市发送邮件的人
    :param index:  转换为 excel 是否包含索引
    :param invest:  是否是投资人报表  如果是 则用投资人邮箱  否则用data邮箱
    :param uuid: 写入 html 时候用的
    :param freeze_panes: 冻结窗口 int 类型 的tuple （行，列 ）
    :param merge_cells: 合并索引
    :param is_df:  主要是看岂不启动  df_dict[k].where(df_dict[k].notnull()

    :return:
    """
    if is_df:
        df_dict = {k: df_dict[k].where(df_dict[k].notnull(), na_rep) for k in df_dict}  # .replace(np.inf, '0')

    # 判断是否是分发文件还是分发 dataFrame
    if file_path is not None:
        # 判断参数 df_dict 类型
        if not isinstance(df_dict, dict):
            df_dict = OrderedDict()
        if not isinstance(colors_dict, dict):
            colors_dict = OrderedDict()
        # 将excel 转为DataFrame  并附带随机颜色
        excel_dict, random_color_dict = excel_util.excel_to_df(file_path, header=header)
        # 添加颜色  和 DataFrame
        df_dict.update(excel_dict)
        colors_dict.update(random_color_dict)

    if not isinstance(df_dict, dict) or df_dict.__len__() == 0:
        raise ValueError("df_dict 必须为长度不为0字典  并且 key 为 sheet_name 单元格名称  value 为 頁面的df")

    if receiver is not None:
        # 总收件人得有吧  就是搜到的是拆分之前的数据
        df_dict_total = OrderedDict()
        for sheet_name in df_dict:
            # 判断传过来的dataFrame 是否是 pd.DataFrame类型   也就是判断上style没
            if isinstance(df_dict[sheet_name], pd.DataFrame) and colors_dict is not None and sheet_name in colors_dict:
                df_dict_total[sheet_name] = style_util.style_color(df_dict[sheet_name],
                                                                   colors=colors_dict[sheet_name],
                                                                   uuid=uuid)
            else:
                df_dict_total[sheet_name] = df_dict[sheet_name]

        # 将要转化为html 的dataFrame 转化成html

        total_file_name = file_name + '_' + date_util.date_to_str(date_util.curdate(0))  # dft='%Y年%m月%d日'
        log.info('处理html，请稍等。。。')

        html = style_util.style_df_to_html(df_dict_total, html_sheet_name, total_file_name, uuid=uuid) \
            if html_sheet_name else None

        log.info('处理excel，请稍等。。。')
        total_title = title + '_' + date_util.date_to_str(date_util.curdate(0))  # dft='%Y年%m月%d日'
        base_file_path = excel_util.pd_to_excel(df_dict=df_dict_total, file_name=total_file_name, engine=engine,
                                                suffix=suffix, na_rep=na_rep, index=index, merge_cells=merge_cells,
                                                freeze_panes=freeze_panes)
        # 发总邮件
        send_files_mail(files=base_file_path, text=text, html=html, title=total_title, receiver=receiver,
                        content_type=content_type, tab=tab, invest=invest)
    else:
        log.warning(" 注意 {} 分城市发送没有主文件接收人。。。".format(file_name))

    if not city or not city_mails:
        log.info('此邮件不分城市发送')
        return

    if isinstance(city_mails, list):
        city_mails = {mail_dict for mail_dict in city_mails}

    for mails_key in city_mails:
        if isinstance(mails_key, str):
            mails = mails_key
            citys = city_mails[mails]
        else:
            raise ValueError('分发邮箱有问题！')

        if isinstance(citys, str):
            citys = [citys]

        if isinstance(citys, tuple):
            citys = list(citys)

        city_title = title if title else ''
        city_file_name = file_name if file_name else ''

        for city in citys:
            city_title += (city + '_')
            city_file_name += (city + '_')
        city_df_dict = OrderedDict()
        city_file_name = city_file_name + date_util.date_to_str(date_util.curdate(0))  # dft='%Y年%m月%d日'
        city_title = city_title + date_util.date_to_str(date_util.curdate(0))  # dft='%Y年%m月%d日'

        for sheet_name in df_dict:
            sheet_df = df_dict[sheet_name]
            # city_key 阔以是index 字符串  也阔以是list集合 当是list时候  里面是标题的的集合
            if city_key == 'index':
                try:
                    city_sheet_df = sheet_df.ix[citys].copy()
                except Exception as e:
                    # city_sheet_df = None
                    log.error("处理出问题！！！！没有找到该城市【{}】".format(citys))
                    continue
            elif isinstance(city_key, list):
                if city_key.__len__() == 1:
                    all_city_df = [sheet_df[sheet_df[city_key[0]] == city].copy() for city in citys]
                    city_sheet_df = pd.concat(all_city_df)
                elif city_key.__len__() == 2:
                    all_city_df = [sheet_df[sheet_df[city_key[0]][city_key[1]] == city].copy() for city in citys]
                    city_sheet_df = pd.concat(all_city_df)
                elif city_key.__len__() == 3:
                    all_city_df = [sheet_df[sheet_df[city_key[0]][city_key[1]][city_key[2]] == city].copy()
                                   for city in citys]
                    city_sheet_df = pd.concat(all_city_df)
                else:
                    raise ValueError('不对劲  city_key 有点多哈')
            elif isinstance(city_key, str):
                all_city_df = [sheet_df[sheet_df[city_key] == city].copy() for city in citys]
                city_sheet_df = pd.concat(all_city_df)

            else:
                raise ValueError('忘记这原来写的是啥来着！先用报错提示吧！')
                # all_city_df = [sheet_df[sheet_df[city_key][city_key] == city].copy() for city in citys]
                # city_sheet_df = pd.concat(all_city_df)
                # city_sheet_df = sheet_df[sheet_df[city_key] in citys].copy()
            if colors_dict is not None and sheet_name in colors_dict and isinstance(city_sheet_df, pd.DataFrame):
                # 上色
                city_sheet_df = style_util.style_color(city_sheet_df, colors=colors_dict[sheet_name], uuid=uuid)
                log.info("【{sheet_name}】添加颜色完成".format(sheet_name=sheet_name))
            if city_sheet_df is not None:
                city_df_dict[sheet_name] = city_sheet_df
        # 生成html
        html = style_util.style_df_to_html(city_df_dict, html_sheet_name, city_file_name,
                                           uuid=uuid) if html_sheet_name else None
        # 转化为excel
        if city_df_dict.__len__() > 0:
            log.info("正在将DataFrame转换成excel，请稍等。。。。")
            city_file_path = excel_util.pd_to_excel(df_dict=city_df_dict, file_name=city_file_name, engine=engine,
                                                    suffix=suffix, na_rep=na_rep, index=index, merge_cells=merge_cells,
                                                    freeze_panes=freeze_panes)

            log.info("发送 {} 数据  文件目录为 ：{}".format(citys, city_file_path))
            # 发这个城市的邮件
            send_files_mail(files=city_file_path, text=text, html=html, title=city_title, receiver=mails, invest=invest)


def recv_mail(search_file, mail_sender=None, extra_days=0):
    log.info('从邮箱收取邮件 ' + search_file + '...')
    server = poplib.POP3_SSL(mail_pop3_)
    server.user(mail_user)
    server.pass_(mail_pass)
    num, size = server.stat()
    today = datetime.date.today()

    path = father_path + '/report_files/'
    mail_files_list = list()
    for i in range(num, max(0, num - 200), -1):
        resp, lines, octets = server.retr(i)
        msg_content = b'\r\n'.join(lines).decode("utf-8")
        msg = Parser().parsestr(msg_content)
        subject = decode_str(msg.get('Subject', ''))
        _sender = decode_str(msg.get('From', ''))
        date = decode_str(msg.get('Date', ''))
        # log.info('msg_content:'+str(msg_content))
        # log.info('subject:'+str(subject))
        # log.info('_sender:'+str(_sender))
        # log.info('date:'+str(date))
        if date is not None and len(str(date)) > 0:
            date = email.utils.parsedate_to_datetime(date)
            if date is not None:
                # t = date_util.utc_to_local(date)
                date = date_util.utc_to_local(date).date()
                if date < today - datetime.timedelta(days=extra_days):
                    break
        if mail_sender is not None and _sender != mail_sender:
            continue
        parts = msg.get_payload()
        found = False
        for n, part in enumerate(parts):
            name = part.get_filename()
            if name is not None:
                name = decode_str(name)
            if name is None or (search_file not in name and search_file not in subject):
                continue
            mail_file_name = path + str(name)
            mail_files_list.append(mail_file_name)
            log.info('收到邮件 ' + subject + ', 日期:' + str(date))
            found = True
            with open(mail_file_name, 'wb') as f:
                f.write(part.get_payload(decode=True))
            break
        if found:
            break
    server.quit()
    if mail_files_list.__len__() == 0:
        log.warning('没有收到邮件')
        return False
    return mail_files_list


if __name__ == '__main__':
    mail_files = recv_mail('【榛果民宿】-KA周报')
    print(mail_files)
